/*
 * Copyright (c) [2020], MediaTek Inc. All rights reserved.
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws.
 * The information contained herein is confidential and proprietary to
 * MediaTek Inc. and/or its licensors.
 * Except as otherwise provided in the applicable licensing terms with
 * MediaTek Inc. and/or its licensors, any reproduction, modification, use or
 * disclosure of MediaTek Software, and information contained herein, in whole
 * or in part, shall be strictly prohibited.
*/
/****************************************************************************
****************************************************************************

	Module Name:
	mbo.c

	Abstract:
	MBO (AP) implementation.

	Revision History:
	Who         When          What
	--------    ----------    ----------------------------------------------
	kyle        2016.8.10   Initial version: MBO IE API
*/

#if defined(MBO_SUPPORT) || defined(OCE_SUPPORT)
#include "rt_config.h"

static MBO_ERR_CODE MboInsertAttrById(
	struct wifi_dev *wdev,
	PUINT8 pAttrTotalLen,
	PUINT8 pAttrBuf,
	UINT8  AttrId
	)
{
	P_MBO_CTRL	pMboCtrl = NULL;
	MBO_ATTR_STRUCT MboAttr;
	PUINT8 pAttrBufOffset = (pAttrBuf + *pAttrTotalLen);
	UINT16 OverflowChk = 0;
#ifdef CONFIG_STA_SUPPORT
	/*UINT8 i;*/
	UINT8 idx;
#endif /* CONFIG_STA_SUPPORT */

	if (wdev) {
		pMboCtrl = &wdev->MboCtrl;
	} else {
		MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"Mbss is NULL !!!!!\n");
		return MBO_INVALID_ARG;
	}

	if (!VALID_MBO_ATTR_ID(AttrId)) {
		MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"Invalid attr Id [%d] !!!!!\n", AttrId);
		return MBO_INVALID_ARG;
	}

	NdisZeroMemory(&MboAttr, sizeof(MBO_ATTR_STRUCT));

	switch (AttrId) {
	case MBO_ATTR_AP_CAP_INDCATION:
		MboAttr.AttrID		= MBO_ATTR_AP_CAP_INDCATION;
		MboAttr.AttrLen	= 1;
		if (pMboCtrl->CellularPreference)
			MboAttr.AttrBody[0] = pMboCtrl->MboCapIndication;
		else
			MboAttr.AttrBody[0] = 0;
		break;
#ifdef CONFIG_STA_SUPPORT
/* yiwei mbo */
/* tmply hard code */
	case MBO_ATTR_STA_NOT_PREFER_CH_REP:
		idx = 0;
		MboAttr.AttrID		= MBO_ATTR_STA_NOT_PREFER_CH_REP;
		MboAttr.AttrLen         = 4;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[0].reg_class;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[0].ch;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[0].pref;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[0].reason_code;
		break;

	case MBO_ATTR_STA_NOT_PREFER_CH_REP_2ND:
		idx = 0;
		MboAttr.AttrID          = MBO_ATTR_STA_NOT_PREFER_CH_REP;
		MboAttr.AttrLen         = 4;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[1].reg_class;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[1].ch;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[1].pref;
		MboAttr.AttrBody[idx++] = pMboCtrl->npc[1].reason_code;
		break;

	case MBO_ATTR_STA_CDC:
		MboAttr.AttrID		= MBO_ATTR_STA_CDC;
		MboAttr.AttrLen	= 0x01;
		MboAttr.AttrBody[0] = 0x01;
		break;
#endif
	case MBO_ATTR_AP_ASSOC_DISALLOW:
		MboAttr.AttrID		= MBO_ATTR_AP_ASSOC_DISALLOW;
		MboAttr.AttrLen	= 1;
		MboAttr.AttrBody[0] = pMboCtrl->AssocDisallowReason;
		break;
	case MBO_ATTR_AP_CDCP:
		MboAttr.AttrID	= MBO_ATTR_AP_CDCP;
		MboAttr.AttrLen	= 1;
		MboAttr.AttrBody[0] = pMboCtrl->CellularPreference;
		break;
	case MBO_ATTR_AP_TRANS_REASON:
		MboAttr.AttrID		= MBO_ATTR_AP_TRANS_REASON;
		MboAttr.AttrLen	= 1;
		MboAttr.AttrBody[0] = pMboCtrl->TransitionReason;
		break;
	case MBO_ATTR_AP_ASSOC_RETRY_DELAY:
		MboAttr.AttrID	= MBO_ATTR_AP_ASSOC_RETRY_DELAY;
		MboAttr.AttrLen		= 2;
		NdisCopyMemory(&MboAttr.AttrBody[0], &pMboCtrl->ReAssocDelay, MboAttr.AttrLen);
		break;
	default:
		MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"UNKNOWN AttrId [%d] !!!!!\n", AttrId);
	}

	OverflowChk = *pAttrTotalLen + MboAttr.AttrLen + 2;
	if (OverflowChk >= MBO_ATTR_MAX_LEN) {
		MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"AttrTotalLen %d Overflow, should be below %d !!!!!\n",
			OverflowChk, MBO_ATTR_MAX_LEN);
		return MBO_UNEXP;
	} else {
		/* safe, insert the attribute */
		NdisCopyMemory(pAttrBufOffset, &MboAttr, MboAttr.AttrLen+2);
		*pAttrTotalLen += (MboAttr.AttrLen + 2);
	}

	return MBO_SUCCESS;
}


static MBO_ERR_CODE MboCollectAttribute(
	PRTMP_ADAPTER pAd,
	struct wifi_dev *wdev,
	PUINT8 pAttrLen,
	PUINT8 pAttrBuf,
	UINT8 FrameType
	)
{
	UINT8 ErrCode = MBO_SUCCESS;

	MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_DEBUG,
		"%s - collect attr for FrameType %d\n", __func__, FrameType);

	if (!pAd || !wdev || !pAttrLen || !pAttrBuf) {
		MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"Invalid input argument !!!!!\n");
		return MBO_INVALID_ARG;
	}

	switch (FrameType) {
#ifdef CONFIG_AP_SUPPORT
	case MBO_FRAME_TYPE_BEACON:
	case MBO_FRAME_TYPE_ASSOC_RSP:
	case MBO_FRAME_TYPE_PROBE_RSP:
		ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_AP_CAP_INDCATION);
		if (!MBO_AP_ALLOW_ASSOC(wdev) && IS_MBO_ENABLE(wdev)) {
			ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_AP_ASSOC_DISALLOW);
		}
		break;
#endif /* CONFIG_AP_SUPPORT */


#ifdef CONFIG_STA_SUPPORT
	case MBO_FRAME_TYPE_ASSOC_REQ:
		ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_STA_NOT_PREFER_CH_REP);
		if (ErrCode != MBO_SUCCESS)
			break;
		ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_STA_NOT_PREFER_CH_REP_2ND);
		if (ErrCode != MBO_SUCCESS)
			break;
		ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_STA_CDC);
		if (ErrCode != MBO_SUCCESS)
			break;
		break;
	case MBO_FRAME_TYPE_PROBE_REQ:
		ErrCode = MboInsertAttrById(wdev, pAttrLen, pAttrBuf, MBO_ATTR_STA_CDC);
		break;
#endif /* CONFIG_STA_SUPPORT */
	default:
		MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"UNKNOWN FrameType %d !!!!!\n", FrameType);
		return MBO_UNEXP;
	}

	return ErrCode;
}

VOID MakeMboOceIE(
	PRTMP_ADAPTER pAd,
	struct wifi_dev *wdev,
	MAC_TABLE_ENTRY *pEntry,
	PUINT8 pFrameBuf,
	PULONG pFrameLen,
	UINT8 FrameType
	)
{
	ULONG	TempLen;
	UINT8	IEId = IE_MBO_ELEMENT_ID;
	UINT8	IELen = 0;
	UINT8	AttrLen = 0;
	UCHAR	MBO_OCE_OUIBYTE[4] = {0x50, 0x6f, 0x9a, 0x16};
	PUCHAR	pAttrBuf = NULL;

	if (wdev == NULL) {
		return;
	}
#ifdef DOT11W_PMF_SUPPORT
	if (wdev->SecConfig.PmfCfg.MFPC == FALSE &&
	IS_AKM_WPA_CAPABILITY(wdev->SecConfig.AKMMap)) {
		MTWF_DBG(NULL, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_DEBUG,
				"%s::Security is WPA2/WPA3, please enable PMF MFPC\n", __func__);
		return;
	}
#endif

	MlmeAllocateMemory(pAd, &pAttrBuf);

	if (pAttrBuf == NULL)
		return;
	else
		NdisZeroMemory(pAttrBuf, MAX_MGMT_PKT_LEN);

#ifdef MBO_SUPPORT
	if (IS_MBO_ENABLE(wdev))
		MboCollectAttribute(pAd, wdev, &AttrLen, pAttrBuf, FrameType);
#endif /* MBO_SUPPORT */

#ifdef OCE_SUPPORT
	if (IS_OCE_ENABLE(wdev))
		OceCollectAttribute(pAd, wdev, pEntry, &AttrLen, pAttrBuf, FrameType);
#endif /* OCE_SUPPORT */
	if (AttrLen) {
		IELen = 4 + AttrLen;

		MakeOutgoingFrame(pFrameBuf,						&TempLen,
						1,								&IEId,
						1,								&IELen,
						4,								MBO_OCE_OUIBYTE,
						AttrLen,						pAttrBuf,
						END_OF_ARGS);

		*pFrameLen = *pFrameLen + TempLen;
	} else {
		MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"AttrLen is zero thus MBO-OCE-IE not added\n");
	}
	MlmeFreeMemory(pAttrBuf);
	return;
}

#ifdef CONFIG_STA_SUPPORT
BOOLEAN MboParseApMboIE(PRTMP_ADAPTER pAd, UCHAR *pAddr, UCHAR *buf, UCHAR len)
{
	UCHAR *pos = NULL;
	UCHAR ParsedLen = 0;
	PEID_STRUCT eid_ptr;
	BOOLEAN bMboAPAssocDisallow = FALSE;

	if (!pAddr) {
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"pAddr is NULL!!!\n");
		return bMboAPAssocDisallow;
	}

	MTWF_DBG(NULL, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_DEBUG,
			"%s - Got MBO IE len [%d]\n", __func__, len);

	pos = buf;

	/* skip OUI 4 bytes */
	pos += 4;
	ParsedLen += 4;

	eid_ptr = (PEID_STRUCT)pos;

	/*
		in empty NPC case , MBO IE contains only OUI and NPC eid 2 len 0.
		so ParsedLen+2 == len
	*/
	while ((ParsedLen + 2) <= len) {
		switch (eid_ptr->Eid) {
		case MBO_ATTR_AP_ASSOC_DISALLOW:
			bMboAPAssocDisallow = TRUE;
			break;
		default:
			MTWF_DBG(NULL, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_DEBUG,
				"%s - ignored MBO_ATTR [%d]\n", __func__, eid_ptr->Eid);
		}

		ParsedLen += (2 + eid_ptr->Len);
		MTWF_DBG(NULL, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_DEBUG, "%s, %u eid_ptr->Len %d ParsedLen %d IE len %d Break %d\n"
				, __func__, __LINE__, eid_ptr->Len, ParsedLen, len, ((ParsedLen+2) < len));
		eid_ptr = (PEID_STRUCT)((UCHAR *)eid_ptr + 2 + eid_ptr->Len);
	}

	return bMboAPAssocDisallow;
}
#endif /* CONFIG_STA_SUPPORT */
MBO_ERR_CODE ReadMboParameterFromFile(
    PRTMP_ADAPTER pAd,
    RTMP_STRING *tmpbuf,
    RTMP_STRING *pBuffer)
{
	INT loop;
	RTMP_STRING *macptr;

    /* MboSupport */
#ifdef CONFIG_AP_SUPPORT
	if (RTMPGetKeyParameter("MboSupport", tmpbuf, MAX_PARAMETER_LEN, pBuffer, TRUE)) {
		for (loop = 0, macptr = rstrtok(tmpbuf, ";");
				(macptr && loop < MAX_MBSSID_NUM(pAd));
					macptr = rstrtok(NULL, ";"), loop++) {
			UINT8 Enable;

			Enable = (UINT8)simple_strtol(macptr, 0, 10);
			pAd->ApCfg.MBSSID[PF_TO_BSS_IDX(pAd, loop)].wdev.MboCtrl.bMboEnable =
				(Enable) ? TRUE : FALSE;
			pAd->ApCfg.MBSSID[PF_TO_BSS_IDX(pAd, loop)].wdev.MboCtrl.MboCapIndication =
				(Enable) ? MBO_AP_CAP_CELLULAR_AWARE : MBO_AP_CAP_NOT_SUPPORT;

			MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"(bMboEnable[%d]=%d, MboCapIndication = 0x%02x)\n", loop,
				pAd->ApCfg.MBSSID[PF_TO_BSS_IDX(pAd, loop)].wdev.MboCtrl.bMboEnable,
				pAd->ApCfg.MBSSID[PF_TO_BSS_IDX(pAd, loop)].wdev.MboCtrl.MboCapIndication);

		}
	}
#endif /*CONFIG_AP_SUPPORT*/

#ifdef CONFIG_STA_SUPPORT
	if (RTMPGetKeyParameter("MboSupport", tmpbuf, MAX_PARAMETER_LEN, pBuffer, TRUE)) {
		for (loop = 0, macptr = rstrtok(tmpbuf, ";");
				(macptr && loop < MAX_MULTI_STA);
					macptr = rstrtok(NULL, ";"), loop++) {
			UINT8 Enable;

			Enable = (UINT8)simple_strtol(macptr, 0, 10);
			pAd->StaCfg[loop].wdev.MboCtrl.bMboEnable =
				(Enable) ? TRUE : FALSE;

			MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				" (bMboEnable[%d]=%d)\n", loop,
				pAd->StaCfg[loop].wdev.MboCtrl.bMboEnable);

		}
	}
#endif /* CONFIG_STA_SUPPORT */
	return MBO_SUCCESS;
}


MBO_ERR_CODE MboInit(
	PRTMP_ADAPTER pAd)
{
	INT loop;
	P_MBO_CTRL pMboCtrl = NULL;

#ifdef CONFIG_AP_SUPPORT
	for (loop = 0; loop < MAX_MBSSID_NUM(pAd); loop++) {
		pMboCtrl = &pAd->ApCfg.MBSSID[loop].wdev.MboCtrl;
		NdisZeroMemory(pMboCtrl, sizeof(MBO_CTRL));
	}
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	for (loop = 0; loop < MAX_MULTI_STA; loop++) {
		pMboCtrl = &pAd->StaCfg[loop].wdev.MboCtrl;
		NdisZeroMemory(pMboCtrl, sizeof(MBO_CTRL));
	}
#endif /* CONFIG_STA_SUPPORT */

	return MBO_SUCCESS;
}

#ifdef CONFIG_AP_SUPPORT
static VOID WextMboSendNeighborReportToDaemonEvent(
	PNET_DEV net_dev,
	P_DAEMON_EVENT_NR_LIST NeighborRepList,
	UINT16 report_buf_len)
{
	struct neighbor_list_data *neighbor_list_data;
	UINT16 buflen = 0;
	char *buf;
	/* 8 = sizeof(neighbor_list_data->ifindex) + sizeof(neighbor_list_data->neighbor_list_len) */
	buflen = 8 + report_buf_len;
	os_alloc_mem(NULL, (UCHAR **)&buf, buflen);
	NdisZeroMemory(buf, buflen);


	neighbor_list_data = (struct neighbor_list_data *)buf;
	neighbor_list_data->ifindex = RtmpOsGetNetIfIndex(net_dev);
	neighbor_list_data->neighbor_list_len	= report_buf_len;
	memcpy(neighbor_list_data->neighbor_list_req, NeighborRepList, report_buf_len);

	RtmpOSWrielessEventSend(net_dev, RT_WLAN_EVENT_CUSTOM,
					OID_NEIGHBOR_REPORT, NULL, (PUCHAR)buf, buflen);

	os_free_mem(buf);
}

static VOID MboUpdateNRElement(
	RTMP_ADAPTER * pAd,
	struct wifi_dev *pWdev,
	BSS_ENTRY *pBssEntry,
	struct _wapp_nr_info *pNeighborEntry,
	UINT16 pMyOwnBssNum)
{
	RRM_BSSID_INFO BssidInfo;
	BOOLEAN bInsertMyOwnBss = (pBssEntry == NULL)?TRUE:FALSE;
	BSS_ENTRY *myOwnBss = NULL;
	BSS_STRUCT *pMbss = &pAd->ApCfg.MBSSID[pMyOwnBssNum];

#ifdef OCE_SUPPORT
	struct _tbtt_info_set TbttInfoSet;
#endif
	UINT8 CondensedPhyType = 0;
	BSS_STRUCT *mbss = &pAd->ApCfg.MBSSID[pMyOwnBssNum];

	pWdev = &pAd->ApCfg.MBSSID[pMyOwnBssNum].wdev;
	os_alloc_mem(NULL, (UCHAR **)&myOwnBss, sizeof(BSS_ENTRY));
	if (myOwnBss == NULL)
		return;
	NdisZeroMemory(myOwnBss, sizeof(BSS_ENTRY));
	if (bInsertMyOwnBss) {
		COPY_MAC_ADDR(pNeighborEntry->Bssid, mbss->wdev.bssid);
		pBssEntry = myOwnBss;

		pBssEntry->CapabilityInfo = pMbss->CapabilityInfo;

		BssidInfo.word = 0;
		BssidInfo.field.APReachAble = 3;
		BssidInfo.field.Security = 1;
		BssidInfo.field.KeyScope = 0;
		BssidInfo.field.SpectrumMng = (pBssEntry->CapabilityInfo & (1 << 8)) ? 1:0;
		BssidInfo.field.Qos = (pBssEntry->CapabilityInfo & (1 << 9)) ? 1:0;
		BssidInfo.field.APSD = (pBssEntry->CapabilityInfo & (1 << 11)) ? 1:0;
		BssidInfo.field.RRM = (pBssEntry->CapabilityInfo & RRM_CAP_BIT) ? 1:0;
		BssidInfo.field.DelayBlockAck = (pBssEntry->CapabilityInfo & (1 << 14)) ? 1:0;
		BssidInfo.field.ImmediateBA = (pBssEntry->CapabilityInfo & (1 << 15)) ? 1:0;
#ifdef DOT11R_FT_SUPPORT
		BssidInfo.field.MobilityDomain = (pWdev->FtCfg.FtCapFlag.Dot11rFtEnable) ? 1:0;
#endif /* DOT11R_FT_SUPPORT */
		BssidInfo.field.HT = WMODE_CAP_N(pWdev->PhyMode) ? 1:0;
#ifdef DOT11_VHT_AC
		BssidInfo.field.VHT = WMODE_CAP_AC(pWdev->PhyMode) ? 1:0;
#endif /* DOT11_VHT_AC */

		if (pWdev->channel > 14) {
			if (BssidInfo.field.HT) {
#ifdef DOT11_VHT_AC
				if (BssidInfo.field.VHT)
					CondensedPhyType = 9;
				else
#endif /* DOT11_VHT_AC */
					CondensedPhyType = 7;
			} else {
				CondensedPhyType = 4;
			}
		} else {
			if (BssidInfo.field.HT)
				CondensedPhyType = 7;
			else if (ERP_IS_NON_ERP_PRESENT(pAd->ApCfg.ErpIeContent))
				CondensedPhyType = 6;
			else if (pBssEntry->SupRateLen > 4)
				CondensedPhyType = 4;
		}

		pNeighborEntry->BssidInfo = BssidInfo.word;
		pNeighborEntry->RegulatoryClass = get_regulatory_class(pAd, pWdev->channel, pWdev->PhyMode, pWdev);
		pNeighborEntry->ChNum = pWdev->channel;
		pNeighborEntry->PhyType = CondensedPhyType;
		pNeighborEntry->TbttInfoSetNum = pAd->ApCfg.BssidNum;
#ifdef OCE_SUPPORT
		TbttInfoSet.NrAPTbttOffset = 255;
		TbttInfoSet.ShortBssid = Crcbitbybitfast(mbss->Ssid, mbss->SsidLen);
		pNeighborEntry->TbttInfoSet = TbttInfoSet;
#endif /* OCE_SUPPORT */
		pNeighborEntry->Rssi = pBssEntry->Rssi;
	} else {
		COPY_MAC_ADDR(pNeighborEntry->Bssid, pBssEntry->Bssid);
		/* update Neighbor Report Information Elements */
		BssidInfo.word = 0;
		BssidInfo.field.APReachAble = 3;
		BssidInfo.field.Security = 1; /* default value, will be updated in daemon */
		BssidInfo.field.KeyScope = 0;
		BssidInfo.field.SpectrumMng = (pBssEntry->CapabilityInfo & (1 << 8)) ? 1:0;
		BssidInfo.field.Qos = (pBssEntry->CapabilityInfo & (1 << 9)) ? 1:0;
		BssidInfo.field.APSD = (pBssEntry->CapabilityInfo & (1 << 11)) ? 1:0;
		BssidInfo.field.RRM = (pBssEntry->CapabilityInfo & RRM_CAP_BIT) ? 1:0;
		BssidInfo.field.DelayBlockAck = (pBssEntry->CapabilityInfo & (1 << 14)) ? 1:0;
		BssidInfo.field.ImmediateBA = (pBssEntry->CapabilityInfo & (1 << 15)) ? 1:0;
		BssidInfo.field.MobilityDomain = (pBssEntry->bHasMDIE) ? 1:0;
		BssidInfo.field.HT = HAS_HT_CAPS_EXIST(pBssEntry->ie_exists) ? 1 : 0;
#ifdef DOT11_VHT_AC
		BssidInfo.field.VHT = HAS_VHT_CAPS_EXIST(pBssEntry->ie_exists) ? 1 : 0;
#endif /* DOT11_VHT_AC */

		if (pBssEntry->Channel > 14) {
			if (HAS_HT_CAPS_EXIST(pBssEntry->ie_exists)) {
#ifdef DOT11_VHT_AC
				if (HAS_VHT_CAPS_EXIST(pBssEntry->ie_exists))
					pBssEntry->CondensedPhyType = 9;
				else
#endif /* DOT11_VHT_AC */
					pBssEntry->CondensedPhyType = 7;
			} else {
				pBssEntry->CondensedPhyType = 4;
			}
		} else {
			if (HAS_HT_CAPS_EXIST(pBssEntry->ie_exists))
				pBssEntry->CondensedPhyType = 7;
			else if (ERP_IS_NON_ERP_PRESENT(pBssEntry->Erp))
				pBssEntry->CondensedPhyType = 6;
			else if (pBssEntry->SupRateLen > 4)
				pBssEntry->CondensedPhyType = 4;
		}

		pNeighborEntry->BssidInfo = BssidInfo.word;
		/* If the NR is from a client, then
		 * pBssEntry->RegulatoryClass would be already set */
		pNeighborEntry->RegulatoryClass = (pBssEntry->RegulatoryClass ?
				pBssEntry->RegulatoryClass :
				get_regulatory_class(pAd, pBssEntry->Channel,
								pWdev->PhyMode, pWdev));
		pNeighborEntry->ChNum = pBssEntry->Channel;
		pNeighborEntry->PhyType = pBssEntry->CondensedPhyType;
		pNeighborEntry->akm = pBssEntry->AKMMap;
		pNeighborEntry->cipher = pBssEntry->PairwiseCipher;
		pNeighborEntry->TbttInfoSetNum = 0;
#ifdef OCE_SUPPORT
		TbttInfoSet.NrAPTbttOffset = 255;
		TbttInfoSet.ShortBssid = Crcbitbybitfast(pBssEntry->Ssid, pBssEntry->SsidLen);
		pNeighborEntry->TbttInfoSet = TbttInfoSet;
#endif /* OCE_SUPPORT */
		pNeighborEntry->Rssi = pBssEntry->Rssi;
	}

	/* Rssi of my own bssid is 0, should convert to the maximum */
	if (pNeighborEntry->Rssi == 0)
		pNeighborEntry->Rssi = 255;

	/* add BSS Transition Candidate Preference subelement - [Subelement ID=3][length=1][preference=20] */
	pNeighborEntry->CandidatePrefSubID  = MBO_RRM_SUBID_BSS_TRANSITION_CANDIDATE_PREFERENCE;
	pNeighborEntry->CandidatePrefSubLen = 1;
	if (bInsertMyOwnBss && !MBO_AP_ALLOW_ASSOC(pWdev))
		pNeighborEntry->CandidatePref = 0;
#ifdef DOT11R_FT_SUPPORT
	else if (pWdev->FtCfg.FtCapFlag.Dot11rFtEnable == TRUE) {
		/* YF_NR */
		pNeighborEntry->CandidatePref = MBO_AP_DEFAULT_CAND_PREF;

		if (bInsertMyOwnBss)
			pNeighborEntry->CandidatePref = 0;
		else if (pBssEntry->bHasMDIE &&
			    (pBssEntry->AKMMap == pWdev->SecConfig.AKMMap) &&
			    (pBssEntry->PairwiseCipher == pWdev->SecConfig.PairwiseCipher) &&
			     NdisCmpMemory(pBssEntry->FT_MDIE.MdId, pWdev->FtCfg.FtMdId, FT_MDID_LEN) == 0) {
			MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_FT, DBG_LVL_INFO, "AP("MACSTR") same in FT Domain\n",
				MAC2STR(pNeighborEntry->Bssid));
			pNeighborEntry->CandidatePref = 255;
		}
	}
#endif /* DOT11R_FT_SUPPORT */
	else
		pNeighborEntry->CandidatePref = MBO_AP_DEFAULT_CAND_PREF;
	if (myOwnBss)
		os_free_mem(myOwnBss);
}

/*	format : iwpriv [interface] set mbo_nr=[append]-[nr_entry_num]
	sample : iwpriv ra0 set mbo_nr=0-12
			==> renew list,not append,indicate 12 entries
*/
INT SetMboNRIndicateProc(
	PRTMP_ADAPTER	pAd,
	RTMP_STRING *arg)
{
	UINT8 i = 0, input = 0, ReportNum = 0;
	RTMP_STRING *macptr;
	BOOLEAN AppendMode = FALSE;
	POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie;
	struct wifi_dev *pWdev = get_wdev_by_ioctl_idx_and_iftype(pAd, pObj->ioctl_if, pObj->ioctl_if_type);

	if (!IS_MBO_ENABLE(pWdev)) {
		MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
				"MBO is disabled %d\n", pWdev->MboCtrl.bMboEnable);
		return FALSE;
	}

	for (i = 0, macptr = rstrtok(arg, "-"); macptr; macptr = rstrtok(NULL, "-"), i++) {
		if (i == 0)
			input = (UINT8) simple_strtol(macptr, 0, 10);
		else if (i == 1)
			ReportNum = (UINT8) simple_strtol(macptr, 0, 10);
		else
			break;
	}

	AppendMode = (input)?TRUE:FALSE;

	MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
						"AppendMode %d , reportNum %d\n",
						AppendMode, ReportNum);

	if (ReportNum == 0 || ReportNum > PER_EVENT_LIST_MAX_NUM)
		ReportNum = PER_EVENT_LIST_MAX_NUM;

	if (IS_MBO_ENABLE(pWdev))
		MboIndicateNeighborReportToDaemon(pAd, pWdev, AppendMode, ReportNum);

	return TRUE;
}

INT MboIndicateNeighborReportToDaemon(
	PRTMP_ADAPTER	pAd,
	struct wifi_dev *pWdev,
	BOOLEAN		AppendMode,
	UINT8			ReportNum)
{
#ifdef DOT11K_RRM_SUPPORT
#ifdef AP_SCAN_SUPPORT
	UINT32 loop = 0;
	DAEMON_EVENT_NR_LIST NeighborRepList;
	BOOLEAN bNewlist = !AppendMode;
	struct _wapp_nr_info *pNeighborEntry = NULL;
	UINT8 TotalReportNum = 0;
	BSS_TABLE *ScanTab = get_scan_tab_by_wdev(pAd, pWdev);

	if (ScanTab == NULL)
		return FALSE;

	if (!IS_RRM_ENABLE(pWdev)) {
		MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"RRM is disabled %d\n", pWdev->RrmCfg.bDot11kRRMEnable);
		return FALSE;
	}

	NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));

	if (ScanTab->BssNr > 0) {

		BssTableSortByRssi(ScanTab, FALSE);

		TotalReportNum = ReportNum < (ScanTab->BssNr + pAd->ApCfg.BssidNum) ?
				ReportNum : (ScanTab->BssNr + pAd->ApCfg.BssidNum);
		NeighborRepList.TotalNum = TotalReportNum;

		/* insert our own bss info into NR list first */
		for (loop = 0; loop < pAd->ApCfg.BssidNum; loop++) {
			if (NeighborRepList.CurrNum < PER_EVENT_LIST_MAX_NUM) {
				pNeighborEntry = &NeighborRepList.EvtNRInfo[NeighborRepList.CurrNum];
				NeighborRepList.CurrNum++;
				/* if AP has multiple bss, add all VAP in neighbor report */
				MboUpdateNRElement(pAd, pWdev, NULL, pNeighborEntry, loop);
			}
			if ((NeighborRepList.CurrNum % PER_EVENT_LIST_MAX_NUM == 0) ||
					(NeighborRepList.CurrNum == pAd->ApCfg.BssidNum)) {
				NeighborRepList.Newlist = bNewlist;
				bNewlist = FALSE;
				/* indicate the last sublist to daemon */
				WextMboSendNeighborReportToDaemonEvent(pWdev->if_dev,
						&NeighborRepList,
						sizeof(DAEMON_EVENT_NR_LIST));

				NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));
			}
		}

		NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));

		for (loop = 0; loop < ScanTab->BssNr; loop++) { /* minus our own NR entry */
			BSS_ENTRY *pBssEntry;
			wapp_nr_info *pNeighborEntry;

			if (loop >= MAX_LEN_OF_BSS_TABLE)
				break;

			if (NeighborRepList.CurrNum >= PER_EVENT_LIST_MAX_NUM)
				break;

			pBssEntry = &ScanTab->BssEntry[loop];

			pNeighborEntry = &NeighborRepList.EvtNRInfo[NeighborRepList.CurrNum];
			NeighborRepList.CurrNum++;
			if (pBssEntry) {
				/* Regulatory class would be determined in
				 * 				 * MboUpdateNRElement() */
				pBssEntry->RegulatoryClass = 0;
				MboUpdateNRElement(pAd, pWdev, pBssEntry, pNeighborEntry, pWdev->func_idx);

				MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
						"append NO. %u len %u BSSID "MACSTR" Chnum %d BssidInfo %X\n"
						"PhyType %x RegulatoryClass %x Privacy %d SSID %s\n",
						loop, (UINT32)sizeof(wapp_nr_info), MAC2STR(pNeighborEntry->Bssid),
						pNeighborEntry->ChNum, pNeighborEntry->BssidInfo,
						pNeighborEntry->PhyType, pNeighborEntry->RegulatoryClass,
						pBssEntry->Privacy, pBssEntry->Ssid);
				hex_dump("neighbor_entry", (UCHAR *)pNeighborEntry, sizeof(wapp_nr_info));
			}

			if ((NeighborRepList.CurrNum > 0) &&
					(NeighborRepList.CurrNum % PER_EVENT_LIST_MAX_NUM == 0)) {
				NeighborRepList.Newlist = bNewlist;
				bNewlist = FALSE;
				/* indicate the sublist to daemon */
				WextMboSendNeighborReportToDaemonEvent(pWdev->if_dev,
						&NeighborRepList,
						sizeof(DAEMON_EVENT_NR_LIST));

				NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));
			}
		}

		if ((NeighborRepList.CurrNum > 0) &&
				(NeighborRepList.CurrNum < PER_EVENT_LIST_MAX_NUM)) {
			NeighborRepList.Newlist = bNewlist;
			bNewlist = FALSE;
			/* indicate the last sublist to daemon */
			WextMboSendNeighborReportToDaemonEvent(pWdev->if_dev,
					&NeighborRepList,
					sizeof(DAEMON_EVENT_NR_LIST));

			NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));
		}
	} else {   /*cert env will really have 0 AP alive, indicate ourself anyway*/
		MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"ScanTab->BssNr=0, indicate our own bssid anyway\n");
		NdisZeroMemory(&NeighborRepList, sizeof(DAEMON_EVENT_NR_LIST));
		TotalReportNum = 1;
		NeighborRepList.TotalNum = TotalReportNum;

		NeighborRepList.Newlist = TRUE;
		/* insert our own bss info into NR list first */
		for (loop = 0; loop < pAd->ApCfg.BssidNum; loop++) {
			if (NeighborRepList.CurrNum < PER_EVENT_LIST_MAX_NUM) {
				pNeighborEntry = &NeighborRepList.EvtNRInfo[NeighborRepList.CurrNum];
				NeighborRepList.CurrNum++;

				MboUpdateNRElement(pAd, pWdev, NULL, pNeighborEntry, loop);
				/* indicate our own bss to daemon */
				if ((NeighborRepList.CurrNum % PER_EVENT_LIST_MAX_NUM == 0) ||
						(NeighborRepList.CurrNum == pAd->ApCfg.BssidNum)) {
					WextMboSendNeighborReportToDaemonEvent(pWdev->if_dev,
							&NeighborRepList,
							sizeof(DAEMON_EVENT_NR_LIST));
					NeighborRepList.Newlist = FALSE;
					NeighborRepList.CurrNum = 0;
				}
			}
		}
	}
#endif /* AP_SCAN_SUPPORT */
#endif /* DOT11K_RRM_SUPPORT */

	return TRUE;
}

INT MBO_MsgHandle(
	IN PRTMP_ADAPTER pAd,
	UINT32 Param,
	UINT32 Value)
{

	POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie;
	UCHAR APIndex = pObj->ioctl_if;
	P_MBO_CTRL pMboCtrl;
	struct wifi_dev *pWdev;

	pMboCtrl = &pAd->ApCfg.MBSSID[APIndex].wdev.MboCtrl;
	pWdev = &pAd->ApCfg.MBSSID[APIndex].wdev;

	if (!IS_MBO_ENABLE(pWdev)) {
		MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"MBO is disabled %d\n", pWdev->MboCtrl.bMboEnable);
		return FALSE;
	}

	switch (Param) {
	case PARAM_MBO_AP_ASSOC_DISALLOW:
		pMboCtrl->AssocDisallowReason = Value;
		break;
	case PARAM_MBO_AP_CAP:
		pMboCtrl->MboCapIndication = Value;
		break;
	case PARAM_MBO_AP_CDCP:
		pMboCtrl->CellularPreference = Value;
		break;
	case PARAM_MBO_AP_BSS_TERM:
		MboBssTermStart(pAd, pWdev, Value);
		break;
	default:
		MTWF_DBG(pAd, DBG_CAT_PROTO, DBG_SUBCAT_ALL, DBG_LVL_ERROR, "Unknown Parameter:%d\n", Param);
		break;
	}
	UpdateBeaconHandler(pAd, pWdev, BCN_UPDATE_IE_CHG);
	return 0;
}

VOID MboWaitAllStaGone(PRTMP_ADAPTER pAd, INT apidx)
{
	UINT i = 0, j = 0;
	PMAC_TABLE_ENTRY pEntry;

	for (j = 0; j < 10; j++) {
		BOOLEAN bSTAIsKeep = FALSE;

		for (i = 0; VALID_UCAST_ENTRY_WCID(pAd, i); i++) {
			pEntry = &pAd->MacTab.Content[i];
			if (pEntry && IS_ENTRY_CLIENT(pEntry)
				&& pEntry->func_tb_idx == apidx && pEntry->IsKeep) {
				bSTAIsKeep = TRUE;
				break;
			}
		}

		if (bSTAIsKeep)
			RtmpOsMsDelay(50);
		else
			return;  /* All Sta Gone , return */
	}

	/* exceed 500 ms */
	return;
}

#endif /* CONFIG_AP_SUPPORT */

static VOID WextMboSendStaInfoToDaemonEvent(
	PNET_DEV pNetDev,
	P_MBO_STA_CH_PREF_CDC_INFO pStaInfo,
	MBO_MSG_TYPE MsgType,
	UINT16 ReportBufLen)
{
	P_MBO_MSG pMboMsg;
	UINT16 buflen = 0;
	char *buf;

	buflen = sizeof(MBO_MSG);
	os_alloc_mem(NULL, (UCHAR **)&buf, buflen);
	NdisZeroMemory(buf, buflen);

	pMboMsg = (P_MBO_MSG)buf;
	pMboMsg->ifindex = RtmpOsGetNetIfIndex(pNetDev);
	pMboMsg->MboMsgLen = ReportBufLen;
	pMboMsg->MboMsgType = MsgType;

	NdisCopyMemory(&pMboMsg->MboMsgBody.MboEvtStaInfo, pStaInfo, ReportBufLen);

	if (MsgType == MBO_MSG_CDC_UPDATE)
		MTWF_DBG(NULL, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
					"%s, indicate STA CDC %d\n",
					__func__, pMboMsg->MboMsgBody.MboEvtStaInfo.cdc);


	MTWF_DBG(NULL, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"%s - sizeof %u report_buf_len %d buflen %u msg_type %s\n",
				__func__, (UINT32)sizeof(MBO_STA_CH_PREF_CDC_INFO), ReportBufLen, buflen, MboMsgTypeToString(MsgType));
	RtmpOSWrielessEventSend(pNetDev, RT_WLAN_EVENT_CUSTOM,
					OID_802_11_MBO_MSG, NULL, (PUCHAR)buf, buflen);

	os_free_mem(buf);
}

INT MboIndicateStaInfoToDaemon(
	PRTMP_ADAPTER	pAd,
	P_MBO_STA_CH_PREF_CDC_INFO pStaInfo,
	MBO_MSG_TYPE MsgType)
{
	/* mac table lookup & update sta's akm/cipher here */
	PMAC_TABLE_ENTRY pEntry = MacTableLookup(pAd, pStaInfo->mac_addr);

	if (pEntry != NULL) {
		pStaInfo->akm = pEntry->SecConfig.AKMMap;
		pStaInfo->cipher = pEntry->SecConfig.PairwiseCipher;
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"\033[1;33m %s, %u pEntry->wcid %d pStaInfo->akm 0x%x pStaInfo->cipher 0x%x\033[0m\n"
			, __func__, __LINE__, pEntry->wcid, pStaInfo->akm, pStaInfo->cipher);
	} else {
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
					"[ERROR] can't find sta entry!!\n");
		return FALSE;
	}

	/* send wext event */
	WextMboSendStaInfoToDaemonEvent(pAd->ApCfg.MBSSID[pEntry->func_tb_idx].wdev.if_dev,
					pStaInfo, MsgType, sizeof(MBO_STA_CH_PREF_CDC_INFO));

	return TRUE;
}

VOID MboParseStaNPCElement(
	PRTMP_ADAPTER pAd,
	struct wifi_dev *pWdev,
	UCHAR *PktContent,
	UINT8 ElementLen,
	P_MBO_STA_CH_PREF_CDC_INFO pMboStaCHInfo,
	MBO_FRAME_TYPE MboFrameType)
{
	/* PktContent starts at Operating Class Field */
	INT8 NpclListLen = 0;
	BOOLEAN bEmptyNPC = FALSE;

	if (MboFrameType == MBO_FRAME_TYPE_WNM_REQ) {
		bEmptyNPC = (ElementLen <= 4)?TRUE:FALSE; /* contains only OUI == EmptyNPC */
		NpclListLen = (ElementLen > 7)?ElementLen - 7:0; /* OUI 4 bytes , Operating Class 1 byte, Pref 1 byte, Reason Code 1 byte */
	} else if (MboFrameType == MBO_FRAME_TYPE_ASSOC_REQ) {
		bEmptyNPC = (ElementLen == 0)?TRUE:FALSE; /* no op class == EmptyNPC */
		NpclListLen = (ElementLen > 3)?ElementLen - 3:0; /* Operating Class 1 byte, Pref 1 byte, Reason Code 1 byte */
	} else {
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"UNKNOWN Frame Type %d , quit.\n", MboFrameType);
		return;
	}


	if (bEmptyNPC) {
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"ElementLen %d indicates no NPC list, All OP-Class CH good.\n"
			, ElementLen);

		pMboStaCHInfo->npc_num = 255;
		return;
	} else {
		UINT8 OperatingClass = *PktContent;
		UCHAR *ChListStart = PktContent + 1;
		UINT8 Preference = *(ChListStart + NpclListLen);
		UINT8 ReasonCode = *(ChListStart + NpclListLen + 1);
		UCHAR *OpChList = get_channelset_by_reg_class(pAd, OperatingClass, pWdev->PhyMode);
		UCHAR OpChListLen = get_channel_set_num(OpChList);
		UINT8 NpcList[MAX_NOT_PREFER_CH_NUM] = {0};
		UINT8 i = 0, j = 0;

		/* to prevent NpclListLen overflow, should not happen  */
		if (NpclListLen > MAX_NOT_PREFER_CH_NUM)
			NpclListLen = MAX_NOT_PREFER_CH_NUM;

		/* to prevent pMboStaCHInfo->npc stack overflow  */
		if (pMboStaCHInfo->npc_num + NpclListLen >= MBO_NPC_MAX_LEN) {
			MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
				"cur_npc_num %d, OpChListLen %d >= MBO_NPC_MAX_LEN %d, return due to overflow.\n",
				pMboStaCHInfo->npc_num, OpChListLen, MBO_NPC_MAX_LEN);
			return;
		}

		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"Got NpclListLen %d, OperatingClass %d, Preference %d, ReasonCode %d OpChListLen %d pMboStaCHInfo->npc_num %d\n",
			NpclListLen, OperatingClass, Preference, ReasonCode,
			OpChListLen, pMboStaCHInfo->npc_num);

		if (OpChListLen == 0)
			MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			" Got OpChListLen %d can't find OperatingClass 0x%x  ERROR!!!!!!!!!!!!!!!!\n",
			OpChListLen, OperatingClass);

		for (i = 0; i < NpclListLen; i++) {
			NpcList[i] = *(ChListStart + i);
			MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"NpcList[%d] = %d\n", i, NpcList[i]);
		}

		/* append channel list to  pMboStaCHInfo */
		for (j = 0; j < OpChListLen; j++) {
			for (i = 0; i < NpclListLen; i++) {
				BOOLEAN bIsNpc = FALSE;
				UINT8 offset = pMboStaCHInfo->npc_num + i;
				P_STA_CH_PREF pNpc = &pMboStaCHInfo->npc[offset];
				if (OpChList[j] == NpcList[i])
					bIsNpc = TRUE;

				/* Update NPC list received only in Assoc, not all channel
				 * list based on operating class received in assoc
				 */
				if (bIsNpc && pNpc != NULL) {
					pNpc->pref = Preference;
					pNpc->ch = OpChList[j];
					pNpc->reason_code = ReasonCode;
					pNpc->reg_class = OperatingClass;
					MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
							"++ final NPC struct [%d] : ch %d , pref %d\n", offset, pNpc->ch, pNpc->pref);
				}
			}
		}

		pMboStaCHInfo->npc_num += NpclListLen;
	}

}

VOID MboParseStaMboIE(PRTMP_ADAPTER pAd, struct wifi_dev *pWdev, struct _MAC_TABLE_ENTRY *pEntry, UCHAR *buf, UCHAR len, MBO_FRAME_TYPE MboFrameType)
{
	UCHAR *pos = NULL;
	UCHAR ParsedLen = 0;
	MBO_STA_CH_PREF_CDC_INFO *pMboStaInfoNPC = NULL;
	MBO_STA_CH_PREF_CDC_INFO *pMboStaInfoCDC = NULL;
	PEID_STRUCT eid_ptr;

	if (!pEntry) {
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
			"pEntry is NULL!!!\n");
		return;
	}

	pMboStaInfoCDC = &pEntry->MboStaInfoCDC;
	pMboStaInfoNPC = &pEntry->MboStaInfoNPC;
	pEntry->bIndicateCDC = FALSE;
	pEntry->bIndicateNPC = FALSE;

	pEntry->bindicate_NPC_event= FALSE;
	pEntry->bindicate_CDC_event = FALSE;

	NdisZeroMemory(pMboStaInfoNPC, sizeof(MBO_STA_CH_PREF_CDC_INFO));
	NdisZeroMemory(pMboStaInfoCDC, sizeof(MBO_STA_CH_PREF_CDC_INFO));

	MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			" Got MBO IE len [%d]\n", len);


	pos = buf;

	/* skip OUI 4 bytes */
	pos += 4;
	ParsedLen += 4;

	eid_ptr = (PEID_STRUCT)pos;

	/*
		in empty NPC case , MBO IE contains only OUI and NPC eid 2 len 0.
		so ParsedLen+2 == len
	*/
	while ((ParsedLen+2) <= len) {
		switch (eid_ptr->Eid) {
		case MBO_ATTR_STA_CDC:
				COPY_MAC_ADDR(pMboStaInfoCDC->mac_addr, pEntry->Addr);
				pMboStaInfoCDC->cdc = eid_ptr->Octet[0];
				pMboStaInfoCDC->npc_num = 0;
				pEntry->bIndicateCDC = TRUE;
				pEntry->bindicate_CDC_event= TRUE;
				break;
		case MBO_ATTR_STA_NOT_PREFER_CH_REP:
				COPY_MAC_ADDR(pMboStaInfoNPC->mac_addr, pEntry->Addr);
				MboParseStaNPCElement(pAd, pWdev, &eid_ptr->Octet[0], eid_ptr->Len, pMboStaInfoNPC, MboFrameType);
				pEntry->bIndicateNPC = TRUE;
				pEntry->bindicate_NPC_event= TRUE;
				MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO,
				"%u npc_num %d mac_addr "MACSTR" bIndicateNPC %d\n",
					__LINE__, pMboStaInfoNPC->npc_num, MAC2STR(pMboStaInfoNPC->mac_addr), pEntry->bIndicateNPC);
				break;
		default:
				MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_ERROR,
					"ignored MBO_ATTR [%d]\n", eid_ptr->Eid);
		}

		ParsedLen += (2 + eid_ptr->Len);
		MTWF_DBG(pAd, DBG_CAT_ALL, DBG_SUBCAT_ALL, DBG_LVL_INFO, "%u eid_ptr->Len %d ParsedLen %d IE len %d Break %d\n",
					__LINE__, eid_ptr->Len, ParsedLen, len, ((ParsedLen+2) < len));
		eid_ptr = (PEID_STRUCT)((UCHAR *)eid_ptr + 2 + eid_ptr->Len);
	}

}

VOID MboIndicateStaBssidInfo(PRTMP_ADAPTER pAd, struct wifi_dev *pWdev, UCHAR *mac_addr)
{
	MBO_STA_CH_PREF_CDC_INFO MboStaInfo;

	NdisZeroMemory(&MboStaInfo, sizeof(MBO_STA_CH_PREF_CDC_INFO));
	COPY_MAC_ADDR(&MboStaInfo.mac_addr, mac_addr);
	COPY_MAC_ADDR(&MboStaInfo.bssid, pWdev->bssid);
	MboIndicateStaInfoToDaemon(pAd, &MboStaInfo, MBO_MSG_BSSID_UPDATE);
}

static VOID WextMboSendStaDisassocToDaemonEvent(
	PNET_DEV pNetDev,
	P_MBO_EVENT_STA_DISASSOC pStaDisassocInfo,
	MBO_MSG_TYPE MsgType,
	UINT16 ReportBufLen)
{
	P_MBO_MSG pMboMsg;
	UINT16 buflen = 0;
	char *buf;

	buflen = sizeof(MBO_MSG);
	os_alloc_mem(NULL, (UCHAR **)&buf, buflen);
	NdisZeroMemory(buf, buflen);

	pMboMsg = (P_MBO_MSG)buf;
	pMboMsg->ifindex = RtmpOsGetNetIfIndex(pNetDev);
	pMboMsg->MboMsgLen = ReportBufLen;
	pMboMsg->MboMsgType = MsgType;

	if (MsgType != MBO_MSG_AP_TERMINATION)
		NdisCopyMemory(&pMboMsg->MboMsgBody.MboEvtStaDisassoc, pStaDisassocInfo, ReportBufLen);

	if (pStaDisassocInfo && MAC_ADDR_EQUAL(pStaDisassocInfo->mac_addr, ZERO_MAC_ADDR)) {
		MTWF_DBG(NULL, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_WARN,
				" ["MACSTR"] sizeof %u \
					report_buf_len %d buflen %d msg_type %s\n",
				MAC2STR(pStaDisassocInfo->mac_addr), (UINT32)sizeof(MBO_EVENT_STA_DISASSOC), ReportBufLen, buflen, MboMsgTypeToString(MsgType));

		RtmpOSWrielessEventSend(pNetDev, RT_WLAN_EVENT_CUSTOM,
					OID_802_11_MBO_MSG, NULL, (PUCHAR)buf, buflen);
	}
	os_free_mem(buf);
}

INT MboIndicateStaDisassocToDaemon(
	PRTMP_ADAPTER	pAd,
	P_MBO_EVENT_STA_DISASSOC pStaDisassocInfo,
	MBO_MSG_TYPE MsgType)
{
	WextMboSendStaDisassocToDaemonEvent(pAd->net_dev, pStaDisassocInfo, MsgType, sizeof(MBO_EVENT_STA_DISASSOC));

	return TRUE;
}

static VOID WextMboSendBssTermToDaemonEvent(
	PNET_DEV pNetDev,
	P_MBO_EVENT_BSS_TERM pBssTermTsf,
	MBO_MSG_TYPE MsgType,
	UINT16 ReportBufLen)
{
	P_MBO_MSG pMboMsg;
	UINT16 buflen = 0;
	char *buf;

	if (pBssTermTsf == NULL)
	return;

	buflen = sizeof(MBO_MSG);
	os_alloc_mem(NULL, (UCHAR **)&buf, buflen);
	NdisZeroMemory(buf, buflen);

	pMboMsg = (P_MBO_MSG)buf;
	pMboMsg->ifindex = RtmpOsGetNetIfIndex(pNetDev);
	pMboMsg->MboMsgLen = ReportBufLen;
	pMboMsg->MboMsgType = MsgType;

	NdisCopyMemory(&pMboMsg->MboMsgBody.MboEvtBssTermTsf, pBssTermTsf, ReportBufLen);

	MTWF_DBG(NULL, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			" sizeof %u report_buf_len %d buflen %d msg_type %s\n",
				(UINT32)sizeof(MBO_EVENT_BSS_TERM), ReportBufLen, buflen, MboMsgTypeToString(MsgType));

	RtmpOSWrielessEventSend(pNetDev, RT_WLAN_EVENT_CUSTOM,
					OID_802_11_MBO_MSG, NULL, (PUCHAR)buf, buflen);
	os_free_mem(buf);
}

RTMP_STRING *MboMsgTypeToString(MBO_MSG_TYPE MsgType)
{
	if (MsgType == MBO_MSG_NEIGHBOR_REPORT)
		return "MBO_MSG_NEIGHBOR_REPORT";
	else if (MsgType == MBO_MSG_STA_PREF_UPDATE)
		return "MBO_MSG_STA_PREF_UPDATE";
	else if (MsgType == MBO_MSG_CDC_UPDATE)
		return "MBO_MSG_CDC_UPDATE";
	else if (MsgType == MBO_MSG_STA_STEERING)
		return "MBO_MSG_STA_STEERING";
	else if (MsgType == MBO_MSG_DISASSOC_STA)
		return "MBO_MSG_DISASSOC_STA";
	else if (MsgType == MBO_MSG_AP_TERMINATION)
		return "MBO_MSG_AP_TERMINATION";
	else if (MsgType == MBO_MSG_BSSID_UPDATE)
		return "MBO_MSG_BSSID_UPDATE";
	else if (MsgType == MBO_MSG_REMOVE_STA)
		return "MBO_MSG_REMOVE_STA";
	else
		return "UNKNOWN MSG TYPE";

}


RTMP_STRING *MboAttrValueToString(UINT8 AttrID, UINT8 AttrValue)
{
	switch (AttrID) {
	case MBO_ATTR_AP_CAP_INDCATION:
		if (AttrValue == MBO_AP_CAP_NOT_SUPPORT)
			return "MBO_AP_CAP_NOT_SUPPORT";
		else if (AttrValue == MBO_AP_CAP_CELLULAR_AWARE)
			return "MBO_AP_CAP_CELLULAR_AWARE";
		else
			return "UNKNOWN ATTR VALUE...";
		break;
	case MBO_ATTR_AP_ASSOC_DISALLOW:
		if (AttrValue == MBO_AP_DISALLOW_REASON_RESERVED)
			return "MBO_AP_ALLOW_ASSOC";
		else if (AttrValue == MBO_AP_DISALLOW_REASON_UNSPECIFIED)
			return "MBO_AP_DISALLOW_REASON_UNSPECIFIED";
		else if (AttrValue == MBO_AP_DISALLOW_MAX_STA_NUM_REACHED)
			return "MBO_AP_DISALLOW_MAX_STA_NUM_REACHED";
		else if (AttrValue == MBO_AP_DISALLOW_AIR_OVERLOADED)
			return "MBO_AP_DISALLOW_AIR_OVERLOADED";
		else if (AttrValue == MBO_AP_DISALLOW_AUTH_SERVER_OVERLOADED)
			return "MBO_AP_DISALLOW_AUTH_SERVER_OVERLOADED";
		else if (AttrValue == MBO_AP_DISALLOW_RSSI_TOO_LOW)
			return "MBO_AP_DISALLOW_RSSI_TOO_LOW";
		else
			return "UNKNOWN ATTR VALUE...";
		break;
	case MBO_ATTR_AP_CDCP:
		if (AttrValue == MBO_AP_CDCP_FORBID_STA_USE_CDC)
			return "MBO_AP_CDCP_FORBID_STA_USE_CDC";
		else if (AttrValue == MBO_AP_CDCP_PREFER_STA_NOT_USE_CDC)
			return "MBO_AP_CDCP_PREFER_STA_NOT_USE_CDC";
		else if (AttrValue == MBO_AP_CDCP_PREFER_STA_USE_CDC)
			return "MBO_AP_CDCP_PREFER_STA_USE_CDC";
		else
			return "UNKNOWN ATTR VALUE...";
		break;
	case MBO_ATTR_AP_TRANS_REASON:
		if (AttrValue == MBO_AP_TRANS_REASON_UNSPECIFIED)
			return "MBO_AP_TRANS_REASON_UNSPECIFIED";
		else if (AttrValue == MBO_AP_TRANS_REASON_TX_PER_TOO_HIGH)
			return "MBO_AP_TRANS_REASON_TX_PER_TOO_HIGH";
		else if (AttrValue == MBO_AP_TRANS_REASON_TRAFFIC_DELAY)
			return "MBO_AP_TRANS_REASON_TRAFFIC_DELAY";
		else if (AttrValue == MBO_AP_TRANS_REASON_INSUFFICIENT_BW)
			return "MBO_AP_TRANS_REASON_INSUFFICIENT_BW";
		else if (AttrValue == MBO_AP_TRANS_REASON_LOAD_BALACING)
			return "MBO_AP_TRANS_REASON_LOAD_BALACING";
		else if (AttrValue == MBO_AP_TRANS_REASON_RSSI_LOW)
			return "MBO_AP_TRANS_REASON_RSSI_LOW";
		else if (AttrValue == MBO_AP_TRANS_REASON_TOO_MANY_RETRY)
			return "MBO_AP_TRANS_REASON_TOO_MANY_RETRY";
		else if (AttrValue == MBO_AP_TRANS_REASON_HIGH_INTRFERENCE)
			return "MBO_AP_TRANS_REASON_HIGH_INTRFERENCE";
		else if (AttrValue == MBO_AP_TRANS_REASON_GRAY_ZONE)
			return "MBO_AP_TRANS_REASON_GRAY_ZONE";
		else if (AttrValue == MBO_AP_TRANS_REASON_TO_PREMIUM_AP)
			return "MBO_AP_TRANS_REASON_TO_PREMIUM_AP";
		else
			return "UNKNOWN ATTR VALUE...";
		break;
	default:
		return "UNKNOWN ATTR VALUE...";
	}
}


INT32 ShowMboStatProc(RTMP_ADAPTER *pAd, RTMP_STRING *arg)
{
	UINT8 loop = 0;
	P_MBO_CTRL pMboCtrl = NULL;
#ifdef CONFIG_AP_SUPPORT
	for (loop = 0; loop < pAd->ApCfg.BssidNum; loop++) {
		pMboCtrl = &pAd->ApCfg.MBSSID[loop].wdev.MboCtrl;

		MTWF_PRINT("========= apidx %d ===========\n", loop);

		MTWF_PRINT("bMboEnable \t\t %d\n",
			pMboCtrl->bMboEnable);
		MTWF_PRINT("AssocDisallowReason \t 0x%x [%s]\n",
			pMboCtrl->AssocDisallowReason, MboAttrValueToString(MBO_ATTR_AP_ASSOC_DISALLOW, pMboCtrl->AssocDisallowReason));
		MTWF_PRINT("CellularPreference \t 0x%x [%s]\n",
			pMboCtrl->CellularPreference, MboAttrValueToString(MBO_ATTR_AP_CDCP, pMboCtrl->CellularPreference));
		MTWF_PRINT("TransitionReason \t 0x%x [%s]\n",
			pMboCtrl->TransitionReason, MboAttrValueToString(MBO_ATTR_AP_TRANS_REASON, pMboCtrl->TransitionReason));
		MTWF_PRINT("ReAssocDelay \t\t 0x%x [second]\n",
			pMboCtrl->ReAssocDelay);
		MTWF_PRINT("MboCapIndication \t\t 0x%x\n",
			pMboCtrl->MboCapIndication);
	}
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	for (loop = 0; loop < MAX_MULTI_STA; loop++) {
		pMboCtrl = &pAd->StaCfg[loop].wdev.MboCtrl;
		MTWF_PRINT("========= StaCfg[%d] ===========\n", loop);
		MTWF_PRINT("bMboEnable \t\t %d\n",
			pMboCtrl->bMboEnable);
	}
#endif /* CONFIG_STA_SUPPORT */

	return TRUE;
}

VOID MboIndicateOneNRtoDaemonByBssEntry(
	PRTMP_ADAPTER pAd,
	struct wifi_dev *pWdev,
	BSS_ENTRY *pBssEntry)
{
	DAEMON_EVENT_NR_LIST NeighborRepList;
	wapp_nr_info *pNeighborEntry = NULL;

	pNeighborEntry = &NeighborRepList.EvtNRInfo[0];
	NdisZeroMemory(pNeighborEntry, sizeof(wapp_nr_info));
	/* Fill the NR entry */
	MboUpdateNRElement(pAd, pWdev, pBssEntry, pNeighborEntry, pWdev->func_idx);
	/* Fill the NR list */
	NeighborRepList.Newlist = FALSE;
    NeighborRepList.TotalNum = 1;
	NeighborRepList.CurrNum = 1;
	/* Send the list to daemon (which contains only one entry) */
	WextMboSendNeighborReportToDaemonEvent(pWdev->if_dev,
						  &NeighborRepList,
						  sizeof(DAEMON_EVENT_NR_LIST));
}

VOID MboBssTermStart(
	PRTMP_ADAPTER pAd,
	struct wifi_dev *pWdev,
	UINT8 countdown
	)
{
	UINT32 hTsf = 0, lTsf = 0, tTsf = 0;
	MBO_EVENT_BSS_TERM bss_term;

	AsicGetTsfTime(pAd, &hTsf, &lTsf, HW_BSSID_0);

	/* Calcuate target TSF */
	/* tTsf = countdown << 20; */ /* sec to usec */
	tTsf = countdown*1000000; /* sec to usec */
	tTsf = lTsf + tTsf;
	if (tTsf < lTsf)
		hTsf++;
	bss_term.TsfLowPart = tTsf;
	bss_term.TsfHighPart = hTsf;

	/* Send an event to daemon */
	/*WextMboSendBssTermToDaemonEvent(pAd->net_dev,*/
	WextMboSendBssTermToDaemonEvent(pWdev->if_dev,
					&bss_term,
					MBO_MSG_AP_TERMINATION,
					sizeof(MBO_EVENT_BSS_TERM));

	/* Trigger BSS Termination count Down */
	pAd->MboBssTermCountDown = countdown + 1; /* plus 1 in case substract immediately */
	pAd->Mbo_Bss_Terminate_Index = pWdev->func_idx;

}

VOID MboCheckBssTermination(
	PRTMP_ADAPTER pAd)
{
	struct wifi_dev *wdev = NULL;
	UCHAR apidx = 0;

	if (pAd->MboBssTermCountDown != 0) {
	    if ((--pAd->MboBssTermCountDown) == 0) {
		wdev = &pAd->ApCfg.MBSSID[0].wdev;
		apidx = pAd->Mbo_Bss_Terminate_Index;
		/* RTMP_BSS_TERMINATION(pAd, wdev); */
		RTEnqueueInternalCmd(pAd, CMDTHREAD_BSS_TERM, (VOID *)&apidx, sizeof(UCHAR));
		}
	}
}

VOID Send_WNM_Notify_Req_toAP(
		IN PRTMP_ADAPTER pAd,
		IN struct wifi_dev *wdev)
{
	UCHAR *Buf = NULL;
	WNM_FRAME *WNMFrame = NULL;
	UINT32 FrameLen = 0, i = 0, idx = 0, chIdx = 0;
	P_MBO_CTRL pMboCtrl = NULL;

	MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_WNM, DBG_LVL_INFO, "\n");

	if (wdev == NULL) {
		MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_WNM, DBG_LVL_ERROR,
				"pWdev %p\n", (void *)wdev);
		return;
	}

	pMboCtrl = &wdev->MboCtrl;

	os_alloc_mem(NULL, (UCHAR **)&Buf, sizeof(WNM_FRAME) + sizeof(pMboCtrl->npc));
	if (Buf == NULL) {
		MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_WNM, DBG_LVL_ERROR,
				"pWdev %p alloc buf fail!\n", (void *)Buf);
		return;
	}
	NdisZeroMemory(Buf, sizeof(WNM_FRAME) + sizeof(pMboCtrl->npc));
	WNMFrame = (WNM_FRAME *)Buf;
	ActHeaderInit(pAd, &WNMFrame->Hdr, wdev->bssid,
			wdev->if_addr,
			wdev->bssid);

	FrameLen += sizeof(HEADER_802_11);
	WNMFrame->Category = CATEGORY_WNM;
	FrameLen += 1;
	WNMFrame->u.WNM_NOTIFY_REQ.Action = WNM_NOTIFICATION_REQ;
	FrameLen += 1;
	WNMFrame->u.WNM_NOTIFY_REQ.DialogToken = RandomByte(pAd);
	FrameLen += 1;
	WNMFrame->u.WNM_NOTIFY_REQ.Type = 221;
	FrameLen += 1;

	for (i = 0; i < MBO_NPC_MAX_LEN; i++) {
		if (pMboCtrl->npc[i].ch) {
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0xdd;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 8;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x50;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x6f;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x9a;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x02;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = pMboCtrl->npc[i].reg_class;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = pMboCtrl->npc[i].ch;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = pMboCtrl->npc[i].pref;
			WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = pMboCtrl->npc[i].reason_code;
			chIdx += 1;
		} else
			break;
	}
	if (chIdx == 0) {
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0xdd;
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 4;
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x50;
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x6f;
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x9a;
		WNMFrame->u.WNM_NOTIFY_REQ.Variable[idx++] = 0x02;
	}
	FrameLen += idx;

	MiniportMMRequest(pAd, 0, Buf, FrameLen);
	os_free_mem(Buf);

	return;
}

/* format : iwpriv [interface] set mbo_ch_pref=[channel]-[preference]-[reason code]
 * sample : iwpriv ra0 set mbo_ch_pref=48-0-2
 *  ==> channel 48 at preference 0 reason code 2
*/
#define PRESET_PREF 1
#define CLEAR_PREF 2
#define CHNG_PREF 3

INT SetMboChPrefProc(
		PRTMP_ADAPTER pAd,
		RTMP_STRING *arg)
{
	UINT8 i = 0, channel = 0, pref = 0, reason_code = 0, reg_class = 0, req_type = 0;
	RTMP_STRING *macptr;
	POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie;
	struct wifi_dev *pWdev = get_wdev_by_ioctl_idx_and_iftype(pAd, pObj->ioctl_if, pObj->ioctl_if_type);
	P_MBO_CTRL pMboCtrl = &pWdev->MboCtrl;

	if (!IS_MBO_ENABLE(pWdev)) {
		MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_WNM, DBG_LVL_ERROR,
				"MBO is disabled %d\n", pWdev->MboCtrl.bMboEnable);
		return FALSE;
	}

	for (i = 0, macptr = rstrtok(arg, "-"); macptr; macptr = rstrtok(NULL, "-"), i++) {
		if (i == 0)
			req_type = (UINT8) simple_strtol(macptr, 0, 10);
		else if (i == 1)
			channel = (UINT8) simple_strtol(macptr, 0, 10);
		else if (i == 2)
			pref = (UINT8) simple_strtol(macptr, 0, 10);
		else if (i == 3)
			reg_class = (UINT8) simple_strtol(macptr, 0, 10);
		else if (i == 4)
			reason_code = (UINT8) simple_strtol(macptr, 0, 10);
		else
			break;
	}

	if (req_type != CLEAR_PREF && i < 5) {
		MTWF_DBG(pAd, DBG_CAT_PROTO, CATPROTO_FT, DBG_LVL_ERROR, "Incorrect parameters\n");
		return -1;
	}

	if (req_type != CLEAR_PREF) {
		for (i = 0; i < MBO_NPC_MAX_LEN; i++) {
			if (pMboCtrl->npc[i].ch > 0)
				continue;
			else {
				pMboCtrl->npc[i].ch = channel;
				pMboCtrl->npc[i].pref = pref;
				pMboCtrl->npc[i].reason_code = reason_code;
				pMboCtrl->npc[i].reg_class = reg_class;
			}
			break;
		}
	} else {
		NdisZeroMemory(pMboCtrl->npc, sizeof(pMboCtrl->npc));
	}

	if (req_type != PRESET_PREF)
		Send_WNM_Notify_Req_toAP(pAd, pWdev);

	MTWF_DBG(pAd, DBG_CAT_CFG, DBG_SUBCAT_ALL, DBG_LVL_INFO,
			"channel %d, pref %d, reason_code %d\n",
			 channel, pref, reason_code);

	return TRUE;
}
#endif /* MBO_SUPPORT OCE_SUPPORT*/
